#include "Tree.h"

/*
 * @resume: construtor da classe Tree
 */
Tree::Tree() {
	this->root = NULL;
}

/*
 * @resume: destrutor da classe Tree
 */
Tree::~Tree() {

}

/*
 * @resume: metodo utilizado para iniciar a raiz
 * @param: string conteudo
 */
void Tree::iniciarRoot(string conteudo) {
	if (this->root == NULL) {
		this->root = new noTree();
	}
	this->root->setNode(0, conteudo);
	this->root->setNodeFather(NULL);
}

/*
 * @resume: metodo utilizado setar a raiz
 * @param: string conteudo
 * @return: noTree*
 */
noTree* Tree::setRoot(string conteudo) {
	if (this->root == NULL) {
		this->root = new noTree();
	}
	this->root->setNode(0, conteudo);
	this->root->setNodeFather(NULL);
	return (this->root);

}

/*
 * @resume: metodo utilizado para adicionar folhas da arvore
 * @param: noTree* nodeFather, string conteudo
 * @return: noTree*
 */
noTree* Tree::addLeaf(noTree* nodeFather, string conteudo) {
	if (nodeFather == NULL) {
		return NULL;
	}
	return (nodeFather->addSon(conteudo));
}

/*
 * @resume: metodo utilizado para adicionar folhas da arvore
 * @param: noTree* nodeSon, noTree* nodeFather
 * @return: noTree*
 */
noTree* Tree::addLeaf(noTree* nodeSon, noTree* nodeFather) {
	if (nodeFather == NULL) {
		return NULL;
	}
	return (nodeFather->addSubTree(nodeSon));
}

/*
 * @resume: metodo utilizado para retornar a raiz da arvore
 * @param: noTree* nodeSon, noTree* nodeFather
 * @return: noTree*
 */
noTree* Tree::getRoot() {
	return (this->root);
}

/*
 * @resume: metodo utilizado para setar o nome do arquivo syntaticTree.txt
 * @param: char* endFileOut
 */
void Tree::setFiles(char* endFileOut) {
	this->fileOut.setEnd(endFileOut);
}

/*
 * @resume: metodo utilizado para iniciar a fila de impressao
 * @return: true: se adicionao
 * 			false: se nao, raiz eh nula
 */
bool Tree::loadPoolQueue() {
	if (this->root == NULL) {
		return (false);
	}

	if (this->poolPrint.size() != 0) {
		this->poolPrint.clear();
	}

	this->addPoolQueue(this->root);

	return (true);
}

/*
 * @resume: metodo utilizado para adicionar node na fila de impressao
 */
void Tree::addPoolQueue(noTree* rootCurrent) {
	list<noTree *>::reverse_iterator it;
	list<noTree *> *sons = NULL;

	if (rootCurrent->hasSons()) {
		sons = rootCurrent->getSons();
		for (it = sons->rbegin(); it != sons->rend(); it++) {
			this->addPoolQueue((*it));
		}
	}

	this->poolPrint.push_back(rootCurrent);
}

/*
 * @resume: metodo utilizado para imprimir a arvore sintatica arquivo de saida (syntaticTree.txt)
 * @param: char* endFileOut
 */
void Tree::showTree(char* fileOut, bool syntaticTree) {
	this->setFiles(fileOut);
	this->fileOut.abrir(fstream::out);
	if (syntaticTree) {

		this->fileOut.write(
				"  ********************************************************************************** \n");
		this->fileOut.write(
				"  *Alunos:     Pedro Sanches Junior                                                * \n");
		this->fileOut.write(
				"  *            Thiago Augusto Lopes Genez                                          * \n");
		this->fileOut.write(
				"  *                                                                                * \n");
		this->fileOut.write(
				"  *Professora: Cintyan                                                             * \n");
		this->fileOut.write(
				"  *                                                                                * \n");
		this->fileOut.write(
				"  *Disciplina: Compiladores                                                        * \n");
		this->fileOut.write(
				"  ********************************************************************************** \n\n");

		this->fileOut.write(
				"  ---------------------------------------------------------------------------------- \n");
		this->fileOut.write(
				" |               A B S T R A C T   S Y N T A X   T R E E                            |\n");
		this->fileOut.write(
				" |__________________________________________________________________________________|\n");
		this->fileOut.write("\n\n");
	}

	if (!this->loadPoolQueue()) {
		this->fileOut.write(
				"  *Arvore Vazia!!!                                                            * \n");
		this->fileOut.write(
				"  ---------------------------------------------------------------------------------- \n");
		this->fileOut.fechar();
		return;
	}

	list<noTree *>::reverse_iterator it;
	list<noTree *>::reverse_iterator it_rev;

	bool print = false;
	string marker = "";

	for (it = this->poolPrint.rbegin(); it != this->poolPrint.rend(); it++) {

		int currentNivel = (*it)->getNivelNode();
		int nivel = 0;
		int nivelAuxiliar = (*it)->getNivelNode();
		bool in = false;
		marker = "";

		if (currentNivel > 0) {

			while (nivel < currentNivel - 1) {
				in = false;
				for (it_rev = it; it_rev != this->poolPrint.rend(); it_rev++) {

					if ((*it_rev)->getNivelNode() == nivelAuxiliar - 1) {
						marker = " |   " + marker;
						in = true;
						break;
					}

					else if ((*it_rev)->getNivelNode() < nivelAuxiliar - 1) {
						break;
					}
				}
				nivelAuxiliar--;
				nivel++;
				if (!in) {
					marker = "     " + marker;
				}
			}

		}

		if (print) {
			print = false;
			this->fileOut.write(" \n");
		}

		if (currentNivel > 0) {
			this->fileOut.write(marker + " |---");
		}

		if ((*it)->hasSons()) {
			this->fileOut.write("[+] ");
		} else {
			this->fileOut.write("--> ");
		}

		if ((*it)->getConteudoSimplesNode().compare("") != 0) {
			this->fileOut.write((*it)->getConteudoSimplesNode());
		}

		else {
			list<string>::iterator itExpressao;
			list<string>*expressao = (*it)->getConteudoExpressaoNode();

			for (itExpressao = expressao->begin(); itExpressao
					!= expressao->end(); itExpressao++) {
				this->fileOut.write((*itExpressao) + "");
			}
		}

		if (((++it)) != this->poolPrint.rend()) {
			if ((*it)->getNivelNode() < currentNivel) {
				this->fileOut.write("\n" + marker + "\n");

			}

			else if ((*it)->getNivelNode() == currentNivel) {
				this->fileOut.write(" \n");
			}

			else if ((*it)->getNivelNode() > currentNivel) {
				print = true;
			}
		}
		it--;

	}
	this->fileOut.write(
			" \n\n---------------------------------------------------------------------------------- \n");
	this->fileOut.fechar();
}
